const {listToMap} = require("./index");
const {groupBy} = require("./index");
const {setProperties} = require("./index");
const {setProperty} = require("./index");
const {getProperty} = require("./index");

class ListProcess {
  list = []

  constructor(list) {
    this.list = list;
  }

  static of(list = []) {
    return new ListProcess(list);
  }

  getDistinctPropertyList(func) {
    const properties = getProperty(this.list, func);
    return ListProcess.of(properties);
  }

  setValueInList(parameters = [], match, set) {
    setProperty(this.list, parameters, match, set)
  }

  setValuesInList(parameters = [], match, set) {
    setProperties(this.list, parameters, match, set);
  }

  filter(match) {
    return ListProcess.of(this.list.filter(match));
  }

  find(match) {
    return Optional.of(this.list.find(match))
  }

  sum(func) {
    return this.list.reduce((pv, cv) => {
      return pv + (func(cv) ? func(cv) : 0)
    }, 0)
  }

  unique() {
    return ListProcess.of([...new Set(this.list)]);
  }

  max(func) {
    const numbers = this.getNumbers(func);
    return Math.max(...numbers);
  }

  min(func) {
    return Math.min(...this.getNumbers(func))
  }

  getNumbers(func) {
    return this.list.map(value => func(value)).filter(value => !Number.isNaN(value));
  }

  average(func) {
    return this.sum(func) / this.list.length;
  }

  toList() {
    return this.list;
  }

  groupBy(func) {
    return groupBy(this.list, func)
  }

  toMap(keyFunc, valFunc) {
    return listToMap(this.list, keyFunc, valFunc);
  }

  split(subGroupLength) {
    let index = 0
    const newArray = []
    while (index < this.list.length) {
      newArray.push(this.list.slice(index, index += subGroupLength))
    }
    return newArray
  }
}

class Optional {
  obj;

  constructor(obj) {
    this.obj = obj;
  }

  static of(obj) {
    return new Optional(obj);
  }

  isPresent() {
    return Boolean(this.obj);
  }

  ifPresent(consumer) {
    if (this.isPresent()) {
      consumer(this.obj)
    }
  }

  orElse(supplier) {
    if (!this.isPresent()) {
      return supplier()
    } else {
      return this.obj;
    }
  }

  orElseThrow(errorSupplier) {
    if (!this.isPresent()) {
      throw errorSupplier();
    } else {
      return this.obj;
    }
  }

  get() {
    return this.obj;
  }
}

module.exports = {
  ListProcess,
  Optional
}